#include <iostream>
#include <string>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/stat.h>
#include <sys/stat.h>
#include <gtest/gtest.h>
#include "../../mq_comm/logger.hpp"

class FileHelper
{
public:
    FileHelper(const std::string& filename) :_filename(filename){}

    /*判断文件是否存在*/
    bool exists()
    {
        struct stat st;
        bzero(&st,sizeof(st));
        if(stat(_filename.c_str(),&st)<0) return false;
        return true;
    }

    /*获取文件大小*/
    size_t size()
    {
        struct stat st;
        bzero(&st,sizeof(st));
        if(stat(_filename.c_str(),&st)<0) return 0;
        return st.st_size;
    }

    /*从指定偏移量处读取指定长度的内容*/
    bool read(char* buf,size_t offset,size_t len)
    {
        std::ifstream ifs(_filename,std::ios::in|std::ios::binary);
        if(ifs.is_open()==false)
        {
            ELOG("%s 文件打开失败",_filename.c_str());
            return false;
        }

        ifs.seekg(offset,std::ios::beg);
        ifs.read(buf,len);
        if(ifs.good()==false)
        {
            ELOG("%s 文件读取失败",_filename.c_str());
            ifs.close();
            return false;
        }

        ifs.close();
        return true;
    }

    /*从文件中读取全部内容*/
    bool read(std::string& body)
    {
        size_t fsize=size();
        body.resize(fsize);
        return read(&body[0],0,fsize);
    }

    /*从指定偏移量处写入指定长度的内容*/
    bool write(const char* buf,size_t offset,size_t len)
    {
        //以读写打开，单纯写打开无法偏移
        std::fstream fs(_filename,std::ios::in|std::ios::out|std::ios::binary);
        if(fs.is_open()==false)
        {
            ELOG("%s 文件打开失败",_filename.c_str());
            return false;
        }

        fs.seekp(offset,std::ios::beg);
        fs.write(buf,len);
        if(fs.good()==false)
        {
            ELOG("%s 文件写入失败",_filename.c_str());
            fs.close();
            return false;
        }
    }

    /*将内容从开始位置写入*/
    size_t write(const std::string& body)
    {
        return write(body.c_str(),0,body.size());
    }

    /*重命名*/
    bool rename(const std::string& new_name)
    {
        return (::rename(_filename.c_str(),new_name.c_str())==0);
    }

    /*创建文件*/
    static bool createFile(const std::string& filename)
    {
        std::fstream ofs(filename,std::ios::binary|std::ios::out);
        if(ofs.is_open()==false)
        {
            ILOG("%s 文件不存在,创建文件",filename.c_str());
            return false;
        }
        ofs.close();
        return true;
    }
    
    /*删除文件*/
    static bool removeFile(const std::string& filename)
    {
        return (::remove(filename.c_str())==0);
    }

    /*获取路径的中的父目录*/
    static std::string parentDirectory(const std::string& filename)
    {
        size_t pos=filename.find_last_of("/");
        if(pos==std::string::npos) return "./";
        return filename.substr(0,pos);
    }

    /*创建目录,考虑到多级目录情况*/
    static bool createDirectory(const std::string& pathname)
    {
        size_t index=0;
        size_t pos=0;
        while(index<pathname.size())
        {
            pos=pathname.find("/",index);
            if(pos==std::string::npos)
            {
                return (::mkdir(pathname.c_str(),0775)==0);
            }

            std::string sub_path=pathname.substr(0,pos);
            int ret=::mkdir(sub_path.c_str(),0775);
            if(ret<0&&errno!=EEXIST)
            {
                ELOG("创建目录 %s 失败: %s",sub_path.c_str(),strerror(errno));
                return false;
            }
            index=pos+1;
        }

        return true;
    }
    static bool removeDirectory(const std::string& path)
    {
        std::string cmd="rm -rf "+path;
        return (::system(cmd.c_str())!=-1);
    }

private:
    std::string _filename;
};

FileHelper helper("./test.txt");
class FileTestEnv:public testing::Environment
{
public:
    virtual void SetUp() override
    {
        std::cout<<"开始测试"<<std::endl;
    }

    virtual void TearDown() override
    {
        std::cout<<"测试结束"<<std::endl;
    }
};

TEST(file_test_env,createFile)
{
    ASSERT_EQ(FileHelper::createFile("./test.txt"),true);
}

TEST(file_test_env,exist)
{
    ASSERT_EQ(helper.exists(),true);
}

TEST(file_test_env,size)
{
    ASSERT_EQ(helper.size(),3);
}

TEST(file_test_env,read)
{
    std::string str;
    str.resize(2);
    helper.read(&str[0],1,2);
    ASSERT_EQ(str,std::string("23"));
    str.clear();
    str.resize(3);
    helper.read(str);
    ASSERT_EQ(str,std::string("123"));
}

TEST(file_test_env,write)
{
    helper.write("456",2,3);
    std::string str;
    helper.read(str);
    ASSERT_EQ(str,std::string("12456"));
}

TEST(file_test_env,removeFile)
{
    ASSERT_EQ(FileHelper::removeFile("./test.txt"),true);
}

TEST(file_test_env,createDirectory)
{
    std::string str=FileHelper::parentDirectory("./aaa/bbb/ccc/test.txt");
    ASSERT_EQ(str,"./aaa/bbb/ccc");
    ASSERT_EQ(FileHelper::createDirectory(str),true);
}

TEST(file_test_env,removeDirectory)
{
    ASSERT_EQ(FileHelper::removeDirectory("aaa"),true);
}

int main(int argc,char* argv[])
{
    testing::AddGlobalTestEnvironment(new FileTestEnv);
    testing::InitGoogleTest(&argc,argv);
    RUN_ALL_TESTS();
    return 0;
}